package com.gframework.core.redis;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.spring.starter.RedissonAutoConfiguration;
import org.redisson.spring.starter.RedissonProperties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.RedisOperations;

import com.gframework.annotation.redis.EnableMultipartRedisson;
import com.gframework.context.spring.AppContext;
import com.gframework.core.redis.MultipartRedissonConfig.RedissonConfig;

/**
 * 多redis配置配置类
 * @author Ghwolf
 * @see EnableMultipartRedisson
 */
@EnableAutoConfiguration(exclude={
		RedissonAutoConfiguration.class,
		RedisAutoConfiguration.class
})
@Import(RedissonConfig.class)
public class MultipartRedissonConfig {
	/**
	 * 多redis spring bean（通过gframework.redis.*进行配置的）名称前缀：{@value}
	 */
	public static final String MULTIPART_REDISSON_BEAN_NAME_PREFIX = "multipart-redis--";


	@Configuration(proxyBeanMethods=false)
	@ConditionalOnClass({Redisson.class, RedisOperations.class})
	@ConditionalOnProperty(value="gframework.redis.enable",havingValue="true")
	@EnableConfigurationProperties({GFrameworkRedisProperties.class,RedissonProperties.class, RedisProperties.class})
	static class RedissonConfig implements ApplicationContextAware{
		
		private final GFrameworkRedisProperties properties ;
		private final RedissonAutoConfiguration defualtRedissonAutoConfiguration ;
		private final List<RedissonClient> redissonClientCache ;
		
		public RedissonConfig(GFrameworkRedisProperties properties, RedissonProperties redissonProperties,
				RedisProperties redisProperties, ApplicationContext applicationContext) {
			this.properties = properties;
			redissonClientCache = new ArrayList<>(properties.getConfig().size());
			defualtRedissonAutoConfiguration = new RedissonAutoConfiguration();
			try {
				Field ctxField = RedissonAutoConfiguration.class.getDeclaredField("ctx");
				Field redisPropertiesField = RedissonAutoConfiguration.class.getDeclaredField("redisProperties");
				Field redissonPropertiesField = RedissonAutoConfiguration.class.getDeclaredField("redissonProperties");
				ctxField.setAccessible(true);
				redisPropertiesField.setAccessible(true);
				redissonPropertiesField.setAccessible(true);
				ctxField.set(defualtRedissonAutoConfiguration, applicationContext);
				redisPropertiesField.set(defualtRedissonAutoConfiguration, redisProperties);
				redissonPropertiesField.set(defualtRedissonAutoConfiguration, redissonProperties);
			} catch (Exception e) {
				throw new BeanInitializationException("MultipartRedissonConfig 初始化失败！",e);
			}
		}
		
		/**
		 * 关闭所有的额外创建的redissson
		 */
		@EventListener(ContextStoppedEvent.class)
		public void destroyOtherRedisson(){
			for (RedissonClient c : redissonClientCache) {
				try {
					c.shutdown();
				} catch(Exception e) {
					// Ignore
				}
			}
		}
	    
	    /**
	     * 主redisson
	     */
	    @Bean(destroyMethod = "shutdown")
	    @Primary
		public RedissonClient redisson() throws IOException {
	    	return this.defualtRedissonAutoConfiguration.redisson();
	    }
		
		@Override
		public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
			if (!properties.isEnable()) {
				return ;
			}
			Field redissonPropertiesField ;
			Field ctxField ;
			try {
				redissonPropertiesField = RedissonAutoConfiguration.class.getDeclaredField("redissonProperties");
				ctxField = RedissonAutoConfiguration.class.getDeclaredField("ctx");
				ctxField.setAccessible(true);
				redissonPropertiesField.setAccessible(true);
			} catch (NoSuchFieldException e) {
				throw new BeanInitializationException("RedissonAutoConfiguration类的ctx与redissonProperties属性获取失败，无法进行批量redis spring bean初始化!",e);
			} catch (SecurityException e) {
				throw new BeanInitializationException("存在安全管理器，无法获取RedissonAutoConfiguration类的ctx与redissonProperties属性！",e);
			}
			
			
			for (Map.Entry<String,RedisProperties> entry : properties.getConfig().entrySet()) {
				RedisProperties pro = entry.getValue();
				
				RedissonAutoConfiguration conf = new RedissonAutoConfiguration();
				try {
					redissonPropertiesField.set(conf, pro);
					ctxField.set(conf, applicationContext);
				} catch (IllegalArgumentException | IllegalAccessException e) {
					throw new BeanInitializationException("设置RedissonAutoConfiguration类的ctx与redissonProperties属性失败，无法进行批量redis spring bean初始化！",e);
				}
				RedissonClient redissonClient;
				try {
					redissonClient = conf.redisson();
				} catch (IOException e) {
					throw new BeanInitializationException("批量redis spring bean初始化异常！",e);
				}
				redissonClientCache.add(redissonClient);
				AppContext.registerBean(MULTIPART_REDISSON_BEAN_NAME_PREFIX + entry.getKey(), redissonClient);
				
			}
			
		}

	}
}
